package jtg.generator;

import jtg.graphics.SootCFG;
import jtg.visualizer.Visualizer;
import soot.Body;
import soot.Local;
import soot.toolkits.graph.UnitGraph;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;


/**
 * RandomGenerator 类用于生成各种类型的随机数据。
 * 它支持基本数据类型、字符串、数组、枚举以及自定义对象类型的随机生成。
 * 该类主要用于测试和模拟数据生成场景，提供了灵活的方式来生成随机测试数据。
 */
public class RandomGenerator extends Generator {

    private Random random;
    private static final String CHAR_SET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+";
    private Map<Class<?>, Object> createdObjects = new HashMap<>();

    public RandomGenerator(String className, String methodName) {
        super(className, methodName);
        String defaultClsPath = System.getProperty("user.dir") + File.separator + "target" + File.separator + "classes";
        new RandomGenerator(defaultClsPath, className, methodName);
    }

    public RandomGenerator(String classPath, String className, String methodName) {
        super(classPath, className, methodName);
        random = new Random();
    }

    @Override
    void drawCFG() {
        drawCFG(clsName + "." + mtdName, false);
    }

    public void drawCFG(String graphName, boolean indexLabel) {
        Visualizer.printCFGDot(graphName, ug, indexLabel);
    }

    /**
     * 获取方法的参数列表。
     * @return 方法的参数列表。
     */
    private List<Local> getParameters() {
        return new ArrayList<>(body.getParameterLocals());
    }

    /**
     * 生成测试用例。
     * @return 测试用例。
     */
    @Override
    public List<String> generate() {
        ArrayList<String> testSet = null;

        System.out.println("============================================================================");
        System.out.println("Generating test case inputs for method: " + clsName + "." + mtdName + "()");
        System.out.println("============================================================================");
        try{
            testSet = new ArrayList<>();
            List<Local> parameters = getParameters();

            for (int i = 0; i < 10; i++) {
                StringBuilder testCase = new StringBuilder();
                for (Local param : parameters) {
                    String paramName = param.getName();
                    String value = generateRandomValueForType(param.getType().toString());
                    testCase.append(paramName).append("=").append(value).append(" ");
                }
                testSet.add(testCase.toString().trim());
            }
        } catch (Exception e) {
            System.err.println("Error in generating test cases: ");
            System.err.println(e.toString());
            e.printStackTrace();
        }
        assert testSet != null;
        if (!testSet.isEmpty()) {
            System.out.println();
            System.out.println("The generated test case inputs:");
            int count = 1;
            for (String tc : testSet) {
                System.out.println("( " + count + " ) " + tc);
                count++;
            }
        }
        return testSet;
    }

    /**
     * 根据类型名生成随机值。
     * @param type 类型名。
     * @return 生成的随机值。
     */
    private String generateRandomValueForType(String type) {
        // 调用RandomGenerator的generate方法，生成随机值
        try {
            Object value = generateRandom(stringToClass(type));
            // 如果是数组类型，指定格式输出
            if (value.getClass().isArray()) {
                StringBuilder sb = new StringBuilder();
                arrayToStringRecursive(value, sb);
                return sb.toString();
            } else {
                return value.toString();
            }
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            System.err.println("Error generating random value for type: " + type);
            System.err.println(e.toString());
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将类型名转换为Class对象。
     * @param typeName 类型名。
     * @return 类型对应的Class对象。
     * @throws ClassNotFoundException 如果找不到指定的类型。
     */
    private Class<?> stringToClass(String typeName) throws ClassNotFoundException {
        int arrayDimension = 0;
        while (typeName.endsWith("[]")) {
            arrayDimension++;
            typeName = typeName.substring(0, typeName.length() - 2);
        }

        Class<?> baseType = getBaseType(typeName);
        return arrayDimension > 0 ? Array.newInstance(baseType, new int[arrayDimension]).getClass() : baseType;
    }

    /**
     * 根据类型名获取基本类型的Class对象。
     * @param typeName 类型名。
     * @return 基本类型的Class对象。
     * @throws ClassNotFoundException 如果找不到指定的类型。
     */
    private Class<?> getBaseType(String typeName) throws ClassNotFoundException {
        switch (typeName) {
            case "boolean":
                return boolean.class;
            case "char":
                return char.class;
            case "byte":
                return byte.class;
            case "short":
                return short.class;
            case "int":
                return int.class;
            case "long":
                return long.class;
            case "float":
                return float.class;
            case "double":
                return double.class;
            case "void":
                return void.class;
            default:
                return Class.forName(typeName);
        }
    }

    private void arrayToStringRecursive(Object array, StringBuilder sb) {
        if (!array.getClass().getComponentType().isArray()) {
            // Base case: one-dimensional array
            sb.append("[");
            int length = Array.getLength(array);
            for (int i = 0; i < length; i++) {
                Object element = Array.get(array, i);
                sb.append(element);
                if (i < length - 1) {
                    sb.append(", ");
                }
            }
            sb.append("]");
        } else {
            // Recursive case: multi-dimensional array
            sb.append("[");
            int length = Array.getLength(array);
            for (int i = 0; i < length; i++) {
                arrayToStringRecursive(Array.get(array, i), sb);
                if (i < length - 1) {
                    sb.append(", ");
                }
            }
            sb.append("]");
        }
    }

    /**
     * 根据提供的类型生成相应的随机数据。
     * @param type 要生成随机数据的类型。
     * @return 生成的随机数据。
     * @throws IllegalAccessException 如果无法访问类的某个字段。
     * @throws InstantiationException 如果无法实例化类。
     */
    public Object generateRandom(Class<?> type) throws IllegalAccessException, InstantiationException {
        int maxLength = 1000;
        if (type.isPrimitive()) {
            return generateRandomPrimitive(type);
        } else if (type.isEnum()) {
            return generateRandomEnum(type);
        } else if (type.equals(String.class)) {
            return generateRandomString(random.nextInt(maxLength) + 1);
        } else if (type.isArray()) {
            return generateRandomArray(type.getComponentType(), random.nextInt(maxLength) + 1);
        } else {
            return generateRandomObject(type);
        }
    }

    /**
     * 生成基本类型的随机值。
     * @param type 基本类型的Class对象。
     * @return 生成的随机基本类型值。
     */
    public Object generateRandomPrimitive(Class<?> type) {
        if (type.equals(Boolean.TYPE)) {
            return random.nextBoolean();
        } else if (type.equals(Character.TYPE)) {
            return CHAR_SET.charAt(random.nextInt(CHAR_SET.length()));
        } else if (type.equals(Byte.TYPE)) {
            return (byte) random.nextInt(Byte.MAX_VALUE + 1);
        } else if (type.equals(Short.TYPE)) {
            return (short) random.nextInt(Short.MAX_VALUE + 1);
        } else if (type.equals(Integer.TYPE)) {
            return random.nextInt();
        } else if (type.equals(Long.TYPE)) {
            return random.nextLong();
        } else if (type.equals(Float.TYPE)) {
            return random.nextFloat();
        } else if (type.equals(Double.TYPE)) {
            return random.nextDouble();
        } else if (type.equals(Void.TYPE)) {
            return null;
        }
        throw new IllegalArgumentException("Unsupported primitive type: " + type);
    }

    /**
     * 生成枚举类型的随机值。
     * @param enumType 枚举类型的Class对象。
     * @return 生成的随机枚举值。
     */
    public Object generateRandomEnum(Class<?> enumType) {
        if (!enumType.isEnum()) {
            throw new IllegalArgumentException(enumType.getName() + " is not an enum type");
        }
        Object[] enumConstants = enumType.getEnumConstants();
        return enumConstants[random.nextInt(enumConstants.length)];
    }

    /**
     * 生成一个随机字符串。
     * @param length 字符串长度。
     * @return 生成的随机字符串。
     */
    public String generateRandomString(int length) {
        return generateRandomString(length, CHAR_SET);
    }

    /**
     * 生成一个随机字符串，使用指定的字符集。
     * @param length 字符串长度。
     * @param alphabet 可用字符集。
     * @return 生成的随机字符串。
     */
    public String generateRandomString(int length, String alphabet) {
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(alphabet.charAt(random.nextInt(alphabet.length())));
        }
        return sb.toString();
    }

    /**
     * 生成一个随机数组。
     * @param componentType 数组元素类型。
     * @param size 数组大小。
     * @return 生成的随机数组。
     */
    public Object generateRandomArray(Class<?> componentType, int size) throws IllegalAccessException, InstantiationException {
        Object array = Array.newInstance(componentType, size);
        for (int i = 0; i < size; i++) {
            Array.set(array, i, generateRandom(componentType));
        }
        return array;
    }

    /**
     * 生成一个随机对象。
     * @param clazz 对象的类类型。
     * @return 生成的随机对象。
     * @throws IllegalAccessException 如果无法访问类的某个字段。
     * @throws InstantiationException 如果无法实例化类。
     */
    public Object generateRandomObject(Class<?> clazz) throws IllegalAccessException, InstantiationException {
        if (createdObjects.containsKey(clazz)) {
            // 返回缓存的实例
            return createdObjects.get(clazz);
        }

        Object instance = clazz.newInstance();
        createdObjects.put(clazz, instance);

        for (Field field : clazz.getDeclaredFields()) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            field.setAccessible(true);
            Class<?> fieldType = field.getType();

            // 检查字段类型是否是基本类型
            if (fieldType.isPrimitive()) {
                // 为基本类型字段生成随机值
                field.set(instance, generateRandomPrimitive(fieldType));
            } else if (fieldType.isEnum()) {
                // 为枚举类型字段生成随机值
                field.set(instance, generateRandomEnum(fieldType));
            } else if (fieldType.equals(String.class)) {
                // 为字符串类型字段生成随机值
                field.set(instance, generateRandomString(random.nextInt(100) + 1));
            } else if (fieldType.isArray()) {
                // 为数组类型字段生成随机值
                field.set(instance, generateRandomArray(fieldType.getComponentType(), random.nextInt(100) + 1));
            } else {
                // 为对象类型字段生成随机值
                field.set(instance, generateRandomObject(fieldType));
            }
        }
        return instance;
    }
}